winsafe\gui\native_controls/edit.rs
1use std::any::Any;
2use std::marker::PhantomPinned;
3use std::pin::Pin;
4use std::sync::Arc;
5
6use crate::co;
7use crate::decl::*;
8use crate::gui::{events::*, privs::*, *};
9use crate::msg::*;
10use crate::prelude::*;
11
12struct EditObj {
13 base: BaseCtrl,
14 events: EditEvents,
15 _pin: PhantomPinned,
16}
17
18native_ctrl! { Edit: EditObj => EditEvents;
19 /// Native
20 /// [edit](https://learn.microsoft.com/en-us/windows/win32/controls/about-edit-controls)
21 /// (text box) control.
22}
23
24impl Edit {
25 /// Instantiates a new `Edit` object, to be created on the parent window
26 /// with [`HWND::CreateWindowEx`](crate::HWND::CreateWindowEx).
27 ///
28 /// # Panics
29 ///
30 /// Panics if the parent window was already created – that is, you cannot
31 /// dynamically create an `Edit` in an event closure.
32 ///
33 /// # Examples
34 ///
35 /// ```no_run
36 /// use winsafe::{self as w, prelude::*, gui};
37 ///
38 /// let wnd: gui::WindowMain; // initialized somewhere
39 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
40 ///
41 /// let txt = gui::Edit::new(
42 /// &wnd,
43 /// gui::EditOpts {
44 /// position: gui::dpi(10, 10),
45 /// width: gui::dpi_x(120),
46 /// ..Default::default()
47 /// },
48 /// );
49 /// ```
50 #[must_use]
51 pub fn new(parent: &(impl GuiParent + 'static), opts: EditOpts) -> Self {
52 let ctrl_id = auto_id::set_if_zero(opts.ctrl_id);
53 let new_self = Self(Arc::pin(EditObj {
54 base: BaseCtrl::new(ctrl_id),
55 events: EditEvents::new(parent, ctrl_id),
56 _pin: PhantomPinned,
57 }));
58
59 let self2 = new_self.clone();
60 let parent2 = parent.clone();
61 parent
62 .as_ref()
63 .before_on()
64 .wm(parent.as_ref().wnd_ty().creation_msg(), move |_| {
65 self2.0.base.create_window(
66 opts.window_ex_style,
67 "EDIT",
68 Some(&opts.text),
69 opts.window_style | opts.control_style.into(),
70 opts.position.into(),
71 SIZE::with(opts.width, opts.height),
72 &parent2,
73 );
74 ui_font::set(self2.hwnd());
75 parent2
76 .as_ref()
77 .add_to_layout(self2.hwnd(), opts.resize_behavior);
78 Ok(0) // ignored
79 });
80
81 new_self.default_message_handlers(parent);
82 new_self
83 }
84
85 /// Instantiates a new `Edit` object, to be loaded from a dialog resource
86 /// with [`HWND::GetDlgItem`](crate::HWND::GetDlgItem).
87 ///
88 /// # Panics
89 ///
90 /// Panics if the parent dialog was already created – that is, you cannot
91 /// dynamically create an `Edit` in an event closure.
92 #[must_use]
93 pub fn new_dlg(
94 parent: &(impl GuiParent + 'static),
95 ctrl_id: u16,
96 resize_behavior: (Horz, Vert),
97 ) -> Self {
98 let new_self = Self(Arc::pin(EditObj {
99 base: BaseCtrl::new(ctrl_id),
100 events: EditEvents::new(parent, ctrl_id),
101 _pin: PhantomPinned,
102 }));
103
104 let self2 = new_self.clone();
105 let parent2 = parent.clone();
106 parent.as_ref().before_on().wm_init_dialog(move |_| {
107 self2.0.base.assign_dlg(&parent2);
108 parent2
109 .as_ref()
110 .add_to_layout(self2.hwnd(), resize_behavior);
111 Ok(true) // ignored
112 });
113
114 new_self.default_message_handlers(parent);
115 new_self
116 }
117
118 fn default_message_handlers(&self, parent: &(impl GuiParent + 'static)) {
119 let self2 = self.clone();
120 let parent2 = parent.clone();
121 parent
122 .as_ref()
123 .before_on()
124 .wm_command(self.ctrl_id(), co::EN::CHANGE, move || {
125 // EN_CHANGE is first sent to the control before CreateWindowEx()
126 // returns, so if the user handles EN_CHANGE, the Edit HWND won't be
127 // set yet. So we set the HWND here.
128 if *self2.hwnd() == HWND::NULL {
129 let hctrl = parent2
130 .as_ref()
131 .hwnd()
132 .GetDlgItem(self2.ctrl_id())
133 .expect(DONTFAIL);
134 self2.0.base.set_hwnd(hctrl);
135 }
136 Ok(())
137 });
138 }
139
140 /// Hides any balloon tip by sending an
141 /// [`em::HideBalloonTip`](crate::msg::em::HideBalloonTip) message.
142 pub fn hide_balloon_tip(&self) -> SysResult<()> {
143 unsafe { self.hwnd().SendMessage(em::HideBalloonTip {}) }
144 }
145
146 /// Limits the number of characters that can be type by sending an
147 /// [`em::SetLimitText`](crate::msg::em::SetLimitText) message.
148 pub fn limit_text(&self, max_chars: Option<u32>) {
149 unsafe {
150 self.hwnd().SendMessage(em::SetLimitText { max_chars });
151 }
152 }
153
154 /// Sets the selection range of the text by sending an
155 /// [`em::SetSel`](crate::msg::em::SetSel) message.
156 ///
157 /// # Examples
158 ///
159 /// Selecting all text in the control:
160 ///
161 /// ```no_run
162 /// use winsafe::{self as w, prelude::*, gui};
163 ///
164 /// let my_edit: gui::Edit; // initialized somewhere
165 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
166 /// # let my_edit = gui::Edit::new(&wnd, gui::EditOpts::default());
167 ///
168 /// my_edit.set_selection(0, -1);
169 /// ```
170 ///
171 /// Clearing the selection:
172 ///
173 /// ```no_run
174 /// use winsafe::gui;
175 ///
176 /// let my_edit: gui::Edit; // initialized somewhere
177 /// # let wnd = gui::WindowMain::new(gui::WindowMainOpts::default());
178 /// # let my_edit = gui::Edit::new(&wnd, gui::EditOpts::default());
179 ///
180 /// my_edit.set_selection(-1, -1);
181 /// ```
182 pub fn set_selection(&self, start: i32, end: i32) {
183 unsafe {
184 self.hwnd().SendMessage(em::SetSel { start, end });
185 }
186 }
187
188 /// Sets the text by calling
189 /// [`HWND::SetWindowText`](crate::HWND::SetWindowText).
190 pub fn set_text(&self, text: &str) -> SysResult<()> {
191 self.hwnd().SetWindowText(text)?;
192 Ok(())
193 }
194
195 /// Displays a balloon tip by sending an
196 /// [`em::ShowBalloonTip`](crate::msg::em::ShowBalloonTip) message.
197 pub fn show_ballon_tip(&self, title: &str, text: &str, icon: co::TTI) -> SysResult<()> {
198 let mut title16 = WString::from_str(title);
199 let mut text16 = WString::from_str(text);
200
201 let mut info = EDITBALLOONTIP::default();
202 info.set_pszTitle(Some(&mut title16));
203 info.set_pszText(Some(&mut text16));
204 info.ttiIcon = icon;
205
206 unsafe { self.hwnd().SendMessage(em::ShowBalloonTip { info: &info }) }
207 }
208
209 /// Retrieves the text by calling
210 /// [`HWND::GetWindowText`](crate::HWND::GetWindowText).
211 #[must_use]
212 pub fn text(&self) -> SysResult<String> {
213 self.hwnd().GetWindowText()
214 }
215}
216
217/// Options to create an [`Edit`](crate::gui::Edit) programmatically with
218/// [`Edit::new`](crate::gui::Edit::new).
219pub struct EditOpts {
220 /// Text of the control to be
221 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
222 ///
223 /// Defaults to empty string.
224 pub text: String,
225 /// Left and top position coordinates of control within parent's client
226 /// area, to be
227 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
228 ///
229 /// Defaults to `gui::dpi(0, 0)`.
230 pub position: (i32, i32),
231 /// Control width to be
232 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
233 ///
234 /// Defaults to `gui::dpi_x(100)`.
235 pub width: i32,
236 /// Control height to be
237 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
238 ///
239 /// Defaults to `gui::dpi_y(23)`.
240 ///
241 /// **Note:** You should change the default height only in a multi-line
242 /// edit, otherwise it will look off.
243 pub height: i32,
244 /// Edit styles to be
245 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
246 ///
247 /// Defaults to `ES::AUTOHSCROLL | ES::NOHIDESEL`.
248 ///
249 /// Suggestions:
250 /// * add `ES::PASSWORD` for a password input;
251 /// * add `ES::NUMBER` to accept only numbers;
252 /// * replace with `ES::MULTILINE | ES::WANTRETURN | ES::AUTOVSCROLL | ES::NOHIDESEL` for a multi-line edit.
253 pub control_style: co::ES,
254 /// Window styles to be
255 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
256 ///
257 /// Defaults to `WS::CHILD | WS::GROUP | WS::TABSTOP | WS::VISIBLE`.
258 pub window_style: co::WS,
259 /// Extended window styles to be
260 /// [created](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexw).
261 ///
262 /// Defaults to `WS_EX::LEFT | WS_EX::CLIENTEDGE`.
263 pub window_ex_style: co::WS_EX,
264
265 /// The control ID.
266 ///
267 /// Defaults to an auto-generated ID.
268 pub ctrl_id: u16,
269 /// Horizontal and vertical behavior of the control when the parent window
270 /// is resized.
271 ///
272 /// **Note:** You should use `Vert::Resize` only in a multi-line edit.
273 ///
274 /// Defaults to `(gui::Horz::None, gui::Vert::None)`.
275 pub resize_behavior: (Horz, Vert),
276}
277
278impl Default for EditOpts {
279 fn default() -> Self {
280 Self {
281 text: "".to_owned(),
282 position: dpi(0, 0),
283 width: dpi_x(100),
284 height: dpi_y(23),
285 control_style: co::ES::AUTOHSCROLL | co::ES::NOHIDESEL,
286 window_style: co::WS::CHILD | co::WS::GROUP | co::WS::TABSTOP | co::WS::VISIBLE,
287 window_ex_style: co::WS_EX::LEFT | co::WS_EX::CLIENTEDGE,
288 ctrl_id: 0,
289 resize_behavior: (Horz::None, Vert::None),
290 }
291 }
292}